Tailwind css 클래스 합성하기 (feat. tailwind-merge)
Tailwind를 사용하다 보면 이미 만들어둔 컴포넌트를 재사용하지만 약간의 스타일 변경을 넣어주고 싶을 때가 있다.
그렇지만, 유틸 클래스의 한계로 만들어진 클래스들의 cascading에 따르기 때문에 개발자가 클래스를 넣어준 순서와 일치하지 않을 수 있다.
.bg-special { --tw-bg-opacity: 1; background-color: rgb(147 129 255 / var(--tw-bg-opacity)); } .bg-blue-500 { --tw-bg-opacity: 1; background-color: rgb(59 130 246 / var(--tw-bg-opacity)); } function SpecailButton() { return <Button className="bg-special" /> } fucntion Button({ className, ...props }) { // 뒤에 오는 클래스인 bg-special 이 적용되었으면 좋겠으나 css의 cascade 규칙상 bg-blue-500이 된다 // 클래스가 생성되는 순서와 태그에 적용되는 순서와 일치하지 않을 수 있다 return <button {...props} className=`bg-blue-500 ${className}` }
그러면 후순위의 클래스들만 뭔가 파싱해서 적용되도록 해주면 되지 않을까..? 했는데 역시 있었다. 아래의 디스커션에서 출처를 얻었다.
https://github.com/tailwindlabs/tailwindcss/discussions/1446#discussioncomment-4459791
적용해보자.
적용
https://github.com/dcastil/tailwind-merge
// 설치 pnpm add -D tailwind-merge
예를 들어 아래와 같은 코드는 위의 예시 처럼 클래스 순서 문제를 야기한다.
<button className="bg-blue-500 bg-gray-500 bg-green-500 bg-red-500"> button </button>
bg-red-500이 적용되었으면 좋겠으나 실제로는 bg-gray-500 으로 적용되었다.
twMerge
함수로 변경해보자.
import { twMerge } from "tailwind-merge"; <button className={twMerge("bg-blue-500 bg-gray-500 bg-green-500 bg-red-500")} > button </button>
실제 태그에 남는 클래스는 bg-red-500만 남았으며 생성되는 클래스도 bg-red-500만 남게 되었다.
어떻게 동작하는 걸까
Tailwind에서 제공하는 클래스들 목록에 대한 설정을 이 라이브러리가 가지고 있다.
https://github.com/dcastil/tailwind-merge/blob/main/src/lib/default-config.ts
예를 들어 간단한 float로 확인해보자.
// https://github.com/dcastil/tailwind-merge/blob/main/src/lib/default-config.ts#L196 float: [{ float: ['right', 'left', 'none'] }],
Tailwind에서 제공하는 right, left, none
만 머지된다는 뜻이다. 실제로 해보자.
<button className={twMerge("float-green float-right float-left")}> button </button>
위에는 float-green
이라는 정의되지 않은 클래스를 넣었다. 과연 머지될까?
정답은 안된다.
위의 기본적으로 정의된 클래스들만 머지되기 때문이다.
그렇다면 기본 설정에서 추가로 설정하려면 어떻게 하고 싶을까? 기본 설정을 확장시킬 수 있는 함수를 제공한다.
https://github.com/dcastil/tailwind-merge/blob/main/docs/api-reference.md#extendtailwindmerge
만약 위와 같이 float-green
이라는 기본적인 tailwind 클래스가 아닌 경우 아래처럼 확장해서 사용하게 된다.
import { extendTailwindMerge } from "tailwind-merge"; const ctwMerge = extendTailwindMerge({ extend: { classGroups: { float: [{ float: ["green"] }], }, }, }); <button className={ctwMerge("float-green float-right float-left")}> button </button>
그러면 아래처럼 잘 머지되게 된다.
그러면 매번 이래야되나..? 싶은데 bg-*
라던지 일반적으로 다양한 값이 올 수 있는 클래스들은 굳이 확장시키지 않더라도 무난하게 사용가능했다.
마무리
사실 이러한 클래스 머지처리는 tailwind-merge 말고도 twin.macro라는 라이브러리가 있는데, twin.macro는 바벨 매크로로 이러한 머지 문제를 해결할 수 있다.
심플하게 설정하고 싶다면 tailwind-merge도 좋은 것 같고, twin.macro로 바벨 매크로를 통해 좀 더 여러 설정을 하는 것도 방법일 수 있다.